﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lichtspiel.Display;
using Lichtspiel.Math;

namespace Lichtspiel.ECSystem
{
    public class Animation : IUpdateable
    {
        private SpriteSheet spriteSheet;
        private int firstFrame;
        private int lastFrame;
        private TimeSpan frameTime;
        private AnimationMode mode;

        private TimeSpan elapsedTime;
        private int currentFrameNumber;

        private bool forwards = true;
        private bool isPlaying = true;

        public event EventHandler<AnimationEventArgs> FrameChanged;
        public event EventHandler<AnimationEventArgs> Finished;

        public Frame CurrentFrame {
            get { return spriteSheet[currentFrameNumber]; }
        }

        public Animation(Frame frame)
            : this(new SpriteSheet(frame.Image, frame.Rectangle), TimeSpan.Zero, AnimationMode.Loop) {

        }

        public Animation(SpriteSheet spriteSheet, TimeSpan frameTime) 
            : this(spriteSheet, frameTime, AnimationMode.Loop) {
            
        }

        public Animation(SpriteSheet spriteSheet, TimeSpan frameTime, AnimationMode mode)
            : this(spriteSheet, 0, spriteSheet.Count-1, frameTime, mode) {
            
        }

        public Animation(SpriteSheet spriteSheet, int firstFrame, int lastFrame, TimeSpan frameTime)
            : this(spriteSheet, firstFrame, lastFrame, frameTime, AnimationMode.Loop) {
            
        }

        public Animation(SpriteSheet spriteSheet, int firstFrame, int lastFrame, TimeSpan frameTime, AnimationMode mode) {
            if (lastFrame < firstFrame) 
                throw new ArgumentException("lastFrame < firstFrame");

            if (firstFrame < 0)
                throw new ArgumentOutOfRangeException("firstFrame");

            if (spriteSheet.Count-1 < lastFrame)
                throw new ArgumentOutOfRangeException("lastFrame", "Spritesheet does not contain enough Frames");

            

            this.spriteSheet = spriteSheet;
            this.firstFrame = firstFrame;
            this.lastFrame = lastFrame;
            this.frameTime = frameTime;
            this.mode = mode;

            currentFrameNumber = firstFrame;
        }

        public void Update(GameTime time) {
            if (isPlaying && frameTime > TimeSpan.Zero && spriteSheet.Count > 1) {
                elapsedTime += time.Elapsed;

                if (elapsedTime >= frameTime) {
                    elapsedTime -= frameTime;

                    if (forwards)
                        currentFrameNumber++;
                    else
                        currentFrameNumber--;

                    if (currentFrameNumber > lastFrame) {
                        switch (mode) {
                            case AnimationMode.Loop:
                                currentFrameNumber = firstFrame;
                                FireFrameChangedEvent(new AnimationEventArgs(CurrentFrame));
                                break;
                            case AnimationMode.PingPong:
                                currentFrameNumber = lastFrame - 1;
                                forwards = !forwards;
                                FireFrameChangedEvent(new AnimationEventArgs(CurrentFrame));
                                break;
                            case AnimationMode.Once:
                                currentFrameNumber = lastFrame;
                                isPlaying = false;
                                FireFinishedEvent(new AnimationEventArgs(CurrentFrame));
                                break;
                            case AnimationMode.OnceHoldFirst:
                                currentFrameNumber = firstFrame;
                                isPlaying = false;
                                FireFinishedEvent(new AnimationEventArgs(CurrentFrame));
                                break;
                        }
                    }

                    if (currentFrameNumber < firstFrame) {
                        switch (mode) {
                            case AnimationMode.PingPong:
                                currentFrameNumber = firstFrame + 1;
                                forwards = !forwards;
                                FireFrameChangedEvent(new AnimationEventArgs(CurrentFrame));
                                break;
                            default:
                                throw new Exception("Unexpected Behavior");
                        }
                    }
                }
            }
        }

        public void FireFrameChangedEvent(AnimationEventArgs args) {
            if (FrameChanged != null) {
                FrameChanged(this, args);
            }
        }

        public void FireFinishedEvent(AnimationEventArgs args) {
            if (Finished != null) {
                Finished(this, args);
            }
        }
    }
}
